package com.suduku.util;

import com.suduku.entity.Box;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 工具类 <br/>
 *
 * @author chena
 */
public class SudoUtil {

    /**
     * 功能描述: 返回标准的list <br/>
     *
     * @param args 参数
     * @return "java.util.List<T>"
     */
    @SafeVarargs
    public static <T> List<T> getList(T...args) {
        List<T> list = Arrays.asList(args);
        return new ArrayList<>(list);
    }

    /**
     * 功能描述: 返回合并后的列表 <br/>
     *
     * @param l1 列表1
     * @param args 列表2
     * @return "java.util.List<T>"
     */
    public static <T> List<T> getList(List<T> l1, T...args) {
        l1.addAll(Arrays.asList(args));
        return l1;
    }

    /**
     * 功能描述: 返回合并后的列表 <br/>
     *
     * @param l1 列表1
     * @param l2 列表2
     * @return "java.util.List<T>"
     */
    public static <T> List<T> getList(List<T> l1, List<T> l2) {
        l1.addAll(l2);
        return l1;
    }

    /**
     * 功能描述: 校验是否数独 <br/>
     * 
     * @param str 字符串 
     */
    public static void isSudoCheck(String str) {
        if(str.length() != 81) {
            throw new RuntimeException("数独长度不为81位");
        }
        if(!str.matches("^[0-9]*$")) {
            throw new RuntimeException("包含非数字字符，请检查");
        }
        // TODO 添加其他校验
    }
    
    /**
     * 功能描述: 转成成单元格列表 <br/>
     * 
     * @param str 字符串
     * @return "java.util.List<com.suduku.entity.Box>"
     */
    public static List<Box> toBoxList(String str) {
        char[] cs = str.toCharArray();
        List<Box> list = new ArrayList<>(cs.length);
        for(int i = 0; i < cs.length; i++) {
            list.add(new Box(cs[i], i));
        }
        return list;
    }

    /**
     * 功能描述: 校验数独是否完成 <br/>
     *
     * @param boxList 数独列表
     * @return "boolean"
     */
    public static boolean isFinish(List<Box> boxList) {
        long count = boxList.stream().filter(Box::isBlank).count();
        // 如果需要，可以添加其他校验
        return count == 0;
    }

    /**
     * 功能描述: 获取指定区域内，不为0的数字列表 <br/>
     *
     * @param areaList 指定区域
     * @return "java.util.List<java.lang.Integer>"
     */
    public static List<Integer> getNotZero(List<Box> areaList) {
        return areaList.stream().filter(Box::isNumber).map(Box::getV).collect(Collectors.toList());
    }

    /**
     * 功能描述: 统计在area区域内，数字v不在当前单元格内出现的个数 <br/>
     * 
     * @param areaList 区域列表
     * @param i 下标
     * @param v 数字
     * @return "long"
     */
    public static long getNumCount(List<Box> areaList, int i, Integer v) {
        return areaList.stream().filter(b -> b.isBlank() && b.getI() != i && b.getCList().contains(v)).count();
    }

    /**
     * 功能描述: 输出数独列表 <br/>
     * 
     * @param boxList 数独
     */
    public static void print(List<Box> boxList) {
        print(boxList, null, null);
    }

    /**
     * 功能描述: 输出数独列表 <br/>
     *
     * @param boxList 数独
     * @param box 单元格
     */
    public static void print(List<Box> boxList, Box box, List<Box> useBox) {
        List<Integer> useIndexs = null;
        if(useBox != null) {
            useIndexs = useBox.stream().map(Box::getI).distinct().collect(Collectors.toList());
        }
        for(Box b : boxList) {
            // 输出填写内容
            if(box != null && b.getI() == box.getI()) {
                System.out.print("{" + b.getV() + "}");
            } else if(useIndexs != null && useIndexs.contains(b.getI())) {
                System.out.print("[" + b.getV() + "]");
            } else {
                System.out.print(" " + b.getV() + " ");
            }
            // 输出待填区域
            System.out.print("(" + padCList(b) + ")");
            // 输出宫-列分隔符
            if((b.getI() + 1) % 3 == 0) {
                System.out.print(" | ");
            }
            // 输出行换行
            if((b.getI() + 1) % 9 == 0) {
                System.out.println();
            }
            // 输出宫-行分隔符
            if((b.getI() + 1) % 27 == 0) {
                System.out.println();
            }
        }
    }

    /**
     * 功能描述: 补全单元格待选数字到9为，如果不够，则填充空格 <br/>
     *
     * @param box 单元格
     * @return "java.lang.String"
     */
    public static String padCList(Box box) {
        if(box.getCList() != null) {
            return padAfter(box.getCList().stream().map(String::valueOf).collect(Collectors.joining("")), ' ', 9);
        }
        return padAfter("", ' ', 9);
    }

    /**
     * 功能描述: 在字符串后面不全指定字符和长度 <br/>
     *
     * @param str 字符串
     * @param c 字符
     * @param length 长度
     * @return "java.lang.String"
     */
    private static String padAfter(String str, char c, int length) {
        if(str == null || "".equals(str)) {
            return repear(c, length);
        }
        if(str.length() <= length) {
            return str + repear(c, length - str.length());
        }
        return str.substring(0, length);
    }

    /**
     * 功能描述: 重复填充字符 <br/>
     * 
     * @param c 填充字符
     * @param count 填充长度
     * @return "java.lang.String"
     */
    private static String repear(char c, int count) {
        if(count <= 0) {
            return "";
        }
        char[] result = new char[count];
        for(int i = 0; i < count; i++) {
            result[i] = c;
        }
        return new String(result);
     }

     /**
      * 功能描述: 判断是相同的单元格列表 <br/>
      *
      * @param b1List 第一个列表
      * @param b2List 第二个列表
      * @return "boolean"
      */
    public static boolean isEqBoxList(List<Box> b1List, List<Box> b2List) {
        // 获取下标，并且相互都相等
        List<Integer> b1Is = b1List.stream().map(Box::getI).collect(Collectors.toList());
        List<Integer> b2Is = b2List.stream().map(Box::getI).collect(Collectors.toList());
        return b1Is.containsAll(b2Is) && b2Is.containsAll(b1Is);
    }
    
    /**
     * 功能描述: 转置 <br/>
     * 
     * @param str 数独字符串
     * @return "java.lang.String"
     */
    public static String transpose(String str) {
        char[] cs = str.toCharArray();
        char[] rs = new char[cs.length];
        for (int i = 0; i < cs.length; i++) {
            // 行列互换
            rs[(i % 9) * 9 + i / 9] = cs[i];
        }
        return new String(rs);
    }

    /**
     * 功能描述: 两个列表的内容是否一致 <br/>
     *
     * @param l1 list1
     * @param l2 list2
     * @return "boolean"
     */
    public static boolean isEqTwoList(List<Integer> l1, List<Integer> l2) {
        return l1.containsAll(l2) && l2.containsAll(l1);
    }

    /**
     * 功能描述: 获取相同的内容 <br/>
     *
     * @param l1 list1
     * @param l2 list2
     * @return "java.lang.Integer"
     */
    public static Integer getEqContent(List<Integer> l1, List<Integer> l2) {
        List<Integer> c = l1.stream().filter(l2::contains).collect(Collectors.toList());
        if(c.size() == 1) {
            return c.get(0);
        }
        return null;
    }

    /**
     * 功能描述: 获取其他的一个数字 <br/>
     *
     * @param list 列表
     * @param i 待排除的数字
     * @return "java.lang.Integer"
     */
    public static Integer getOther(List<Integer> list, Integer i) {
        List<Integer> c = list.stream().filter(l -> !l.equals(i)).collect(Collectors.toList());
        if(c.size() == 1) {
            return c.get(0);
        }
        return null;
    }

    /**
     * 功能描述: 获取列表1中，不在列表2中的数字 <br/>
     *
     * @param l1 列表1
     * @param l2 列表2
     * @return "java.lang.Integer"
     */
    public static Integer getOther(List<Integer> l1, List<Integer> l2) {
        List<Integer> c = l1.stream().filter(l -> !l2.contains(l)).collect(Collectors.toList());
        if(c.size() == 1) {
            return c.get(0);
        }
        return null;
    }

    /**
     * 功能描述: 查找单元格列表 <br/>
     *
     * @param list 列表
     * @param n 候选数字
     * @return "java.util.List<com.suduku.entity.Box>"
     */
    public static List<Box> findBoxByCList(List<Box> list, Integer n) {
        return list.stream()
                .filter(b -> b.isBlank() && b.getCList().contains(n))
                .collect(Collectors.toList());
    }

    /**
     * 功能描述: 是否梯形 <br/>
     *
     * b1----b2
     * |      \
     * |       \
     * |        \
     * b3--------b4
     *
     * @param b1 第一条平行边第一个点，与b3形成直角边
     * @param b2 第一条平行边第二个点，与b4形成斜边
     * @param b3 第二条平行边第一个点，与b1形成直角边
     * @param b4 第二条平行边第二个点，与b2形成斜边
     * @return "boolean"
     */
    public static boolean isYTrapezoid(Box b1, Box b2, Box b3, Box b4) {
        return isTrapezoid(b1, b2, b3, b4, true);
    }

    /**
     * 功能描述: 是否梯形 <br/>
     *
     * b1------b3
     * |       |
     * |       |
     * |       b4
     * |
     * b2
     *
     * @param b1 第一条平行边第一个点，与b3形成直角边
     * @param b2 第一条平行边第二个点，与b4形成斜边
     * @param b3 第二条平行边第一个点，与b1形成直角边
     * @param b4 第二条平行边第二个点，与b2形成斜边
     * @return "boolean"
     */
    public static boolean isXTrapezoid(Box b1, Box b2, Box b3, Box b4) {
        return isTrapezoid(b1, b2, b3, b4, false);
    }

    /**
     * 功能描述: 是否梯形 <br/>
     *
     * @param b1 第一条平行边第一个点
     * @param b2 第一条平行边第二个点
     * @param b3 第二条平行边第一个点
     * @param b4 第二条平行边第二个点
     * @param isY 判断形状
     * @return "boolean"
     */
    public static boolean isTrapezoid(Box b1, Box b2, Box b3, Box b4, boolean isY) {
        return b1.getG() != b3.getG()
                && isY ? b1.getY() == b3.getY() && b2.getGy() == b4.getGy() : b1.getX() == b3.getX() && b2.getGx() == b4.getGx();
    }

    /**
     * 功能描述: 是否梯形 <br/>
     *
     * @param b1 第一条平行边第一个点
     * @param b2 第一条平行边第二个点
     * @param b3 第二条平行边第一个点
     * @param b4 第二条平行边第二个点
     * @param getFun 通过方法判断，传递getY 或 getX 进行区分 代替gx和gy
     * @return "boolean"
     */
    public static boolean isTrapezoid(Box b1, Box b2, Box b3, Box b4, Function<Box, Integer> getFun) {
        return b1.getG() != b3.getG() && getFun.apply(b1).equals(getFun.apply(b3)) && (getFun.apply(b2) / 3) == (getFun.apply(b4) / 3);
    }

    /**
     * 功能描述: 判断是否不在同一宫中的两个单元格 <br/>
     *
     * @param bList 待判断单元格列表
     * @return "boolean"
     */
    public static boolean isTwoBox(List<Box> bList) {
        return bList.size() == 2 && bList.get(0).getG() != bList.get(1).getG();
    }

    /**
     * 功能描述: 发现数对 <br/>
     *
     * @param towCLists 待发现的数对
     * @return "java.util.List<com.suduku.entity.Box>"
     */
    public static List<Box> findSuDuiBoxList(List<Box> towCLists) {
        for(Box b1 : towCLists) {
            for(Box b2 : towCLists) {
                if(b1.isBlank() && b2.isBlank() && b1.getI() != b2.getI()) {
                    if(b1.getCList().size() == 2 && isEqTwoList(b1.getCList(), b2.getCList())) {
                        return getList(b1, b2);
                    }
                }
            }
        }
        return null;
    }

    /**
     * 功能描述: 是否两个单元格在一个宫内，并且另外一个不在该宫内 <br/>
     *
     * @param list 列表
     * @return "boolean"
     */
    public static boolean isTwoBoxAndOne(List<Box> list) {
        // B1B2 | B3
        return list.size() == 3
                && ((list.get(0).getG() == list.get(1).getG() && list.get(0).getG() != list.get(2).getG())
                    // B1 | B2B3
                    || (list.get(0).getG() != list.get(1).getG() && list.get(1).getG() == list.get(2).getG()));
    }
}
